#include "executor.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <iostream>

// setuptestcase fixture
// setup everycase

#define NOT_ALERT 0
#define GOT_ALERT 1

int g_alertFlag = NOT_ALERT;

class ExecutorTest : public ::testing::Test {
  protected:
    virtual void SetUp() { std::cout << "setup executor" << std::endl; }
    virtual void TearDown()
    {
        std::cout << "teardown executor" << std::endl;
        Executor *e = executor_create();
        ExecutorInit(e);
        g_alertFlag = NOT_ALERT;
    }
    static void SetUpTestCase() { std::cout << "setup by case" << std::endl; }
    static void TearDownTestCase()
    {
        std::cout << "teardown by case" << std::endl;
    }
};

TEST_F(ExecutorTest, default_at_0_0_North)
{
    std::cout << "default at 0 0 north" << std::endl;
    Postion tmp = {0};
    std::cout << "tmp.x:" << tmp.x << std::endl;
    Executor *e = executor_create();
    Postion   p = executor_get_pos(e);
    ASSERT_EQ(0, p.x);
    ASSERT_EQ(0, p.y);
    ASSERT_EQ(NORTH, e->heading);
}

TEST_F(ExecutorTest, default_move_forward)
{
    std::cout << "default move forward" << std::endl;
    Executor *e = executor_create();
    Postion   p = executor_get_pos(e);
    ASSERT_EQ(0, p.x);
    ASSERT_EQ(0, p.y);
    ASSERT_EQ(NORTH, e->heading);

    Forward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(0, p.x);
    ASSERT_EQ(1, p.y);
    ASSERT_EQ(NORTH, e->heading);

    Backward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(0, p.x);
    ASSERT_EQ(0, p.y);
    ASSERT_EQ(NORTH, e->heading);

    TurnRound(e);
    p = executor_get_pos(e);
    ASSERT_EQ(0, p.x);
    ASSERT_EQ(0, p.y);
    ASSERT_EQ(SOUTH, e->heading);

    Backward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(0, p.x);
    ASSERT_EQ(1, p.y);
    ASSERT_EQ(SOUTH, e->heading);

    // is the same before
    int shouldX = 0;
    int shouldY = 1;

    Forward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(--shouldY, p.y);
    ASSERT_EQ(SOUTH, e->heading);
}
TEST_F(ExecutorTest, move_backward)
{
    std::cout << "default move forward" << std::endl;
    Executor *e       = executor_create();
    Postion   p       = executor_get_pos(e);
    int       shouldX = 0;
    int       shouldY = 0;
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(shouldY, p.y);
    ASSERT_EQ(NORTH, e->heading);

    Backward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(--shouldY, p.y);
    ASSERT_EQ(NORTH, e->heading);

    e->heading = SOUTH;
    Backward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(++shouldY, p.y);
    ASSERT_EQ(SOUTH, e->heading);

    e->heading = EAST;
    Backward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(--shouldX, p.x);
    ASSERT_EQ(shouldY, p.y);
    ASSERT_EQ(EAST, e->heading);

    e->heading = WEST;
    Backward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(++shouldX, p.x);
    ASSERT_EQ(shouldY, p.y);
    ASSERT_EQ(WEST, e->heading);
}

TEST_F(ExecutorTest, move_forward)
{
    std::cout << "default move forward" << std::endl;
    Executor *e       = executor_create();
    Postion   p       = executor_get_pos(e);
    int       shouldX = 0;
    int       shouldY = 0;
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(shouldY, p.y);
    ASSERT_EQ(NORTH, e->heading);

    Forward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(++shouldY, p.y);
    ASSERT_EQ(NORTH, e->heading);

    e->heading = SOUTH;
    Forward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(--shouldY, p.y);
    ASSERT_EQ(SOUTH, e->heading);

    e->heading = EAST;
    Forward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(++shouldX, p.x);
    ASSERT_EQ(shouldY, p.y);
    ASSERT_EQ(EAST, e->heading);

    e->heading = WEST;
    Forward(e);
    p = executor_get_pos(e);
    ASSERT_EQ(--shouldX, p.x);
    ASSERT_EQ(shouldY, p.y);
    ASSERT_EQ(WEST, e->heading);
}

TEST_F(ExecutorTest, turnRound)
{
    std::cout << "default move forward" << std::endl;
    Executor *e       = executor_create();
    Postion   p       = executor_get_pos(e);
    int       shouldX = 0;
    int       shouldY = 0;
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(shouldY, p.y);
    ASSERT_EQ(NORTH, e->heading);

    TurnRound(e);
    p = executor_get_pos(e);
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(shouldY, p.y);
    ASSERT_EQ(SOUTH, e->heading);

    TurnRound(e);
    p = executor_get_pos(e);
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(shouldY, p.y);
    ASSERT_EQ(NORTH, e->heading);

    e->heading = EAST;
    TurnRound(e);
    p = executor_get_pos(e);
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(shouldY, p.y);
    ASSERT_EQ(WEST, e->heading);

    TurnRound(e);
    p = executor_get_pos(e);
    ASSERT_EQ(shouldX, p.x);
    ASSERT_EQ(shouldY, p.y);
    ASSERT_EQ(EAST, e->heading);
}

void TestAlertFunc(int type, int x, int y)
{
    g_alertFlag = GOT_ALERT;
    std::cout << "alert type:" << type << " x:" << x << " y:" << y << std::endl;
}

void TestCleanFunc(int x, int y)
{
    std::cout << "clean:"
              << " x:" << x << " y:" << y << std::endl;
}

TEST_F(ExecutorTest, default_sprint3_alert)
{
    std::cout << "default move forward" << std::endl;
    Executor *e = executor_create();
    Postion   p = executor_get_pos(e);
    ASSERT_EQ(0, p.x);
    ASSERT_EQ(0, p.y);
    ASSERT_EQ(NORTH, e->heading);

    RegAlertFunc(TestAlertFunc);
    ASSERT_EQ(g_alertFlag, NOT_ALERT);
    Postion alertPos = {0, -1};
    SetAlertPos(e, alertPos);

    Backward(e);

    ASSERT_EQ(g_alertFlag, GOT_ALERT);
}

TEST_F(ExecutorTest, default_sprint3_2) { ASSERT_EQ(g_alertFlag, NOT_ALERT); }

TEST_F(ExecutorTest, default_sprint4_clean)
{
    ASSERT_EQ(g_alertFlag, NOT_ALERT);

    std::cout << "default move forward" << std::endl;
    Executor *e = executor_create();
    Postion   p = executor_get_pos(e);
    ASSERT_EQ(0, p.x);
    ASSERT_EQ(0, p.y);
    ASSERT_EQ(NORTH, e->heading);

    RegCleanFunc(TestCleanFunc);
    Postion alertPos = {0, 1};
    SetCleanPos(e, alertPos);

    Forward(e);
}
